from django.shortcuts import render,redirect
#导包
from django.http import HttpResponse,HttpResponseRedirect,JsonResponse
#导入类视图
# from django.utils.deprecation import MiddlewareMixin
from django.views import View

#from myapp.models import User
import json
from django.core.serializers import serialize
from rest_framework.response import Response
from rest_framework.views import APIView
#导入加密库
import hashlib
#导入图片库
#绘画库
from PIL import ImageDraw
#字体库
from PIL import ImageFont
#图片库
from PIL import Image
#随机库
import random
#文件流
import io

import requests

#导入上传文件夹配置

from mydjango.settings import UPLOAD_ROOT
import os

#导入原生sql模块
from django.db import connection

import jwt

#导入redis数据库
import redis

#导入时间模块
import time

#导入公共目录变量
from mydjango.settings import BASE_DIR

#导包
from django.db.models import Q,F

#导入dwebsocket的库
from dwebsocket.decorators import accept_websocket
import uuid

from myapp.models import *
from myapp.myser import *

# redis
import redis
# 定义IP地址和端口
host = '127.0.0.1'
port = 6379
# 定义链接对象
r = redis.Redis(host=host, port=port)

# 导包
from django.utils.deprecation import MiddlewareMixin
# 自定义中间件
class MyMiddleWare(MiddlewareMixin):
    # 请求之前
    def process_reqeust(self, request):
        # uid = request.GET.get('uid')
        # clinet_jwt = request.GET.get('jwt', None)
        # if clinet_jwt == None:
        #     return HttpResponse('你没有带上令牌')
        #
        # print(uid)
        # # 解密
        # decode_jwt = jwt.decode(clinet_jwt, '123', algorithms=['HS256'])
        # if decode_jwt['uid'] != str(uid):
        #     return HttpResponse('你篡改了用户ID')

        pass
    # 模板渲染之前
    def process_view(self, request, view_func, view_args, view_kwargs):
        pass
    # 请求之后
    def process_response(self, request, response):
        return response

# 工具方法  获取前N名的数据
def get_top_n(num):
    # 获取redis数据
    goods_click = r.zrange('good_rank', 0, -1, desc=True, withscores=True)[:num]

    # 获取mysql中数据
    goods = Goods.objects.in_bulk([int(item[0]) for item in goods_click])

    # 合并操作
    res = []
    for item in goods_click:
        # 遍历列表  遍历中就添加
        # 捕获异常  防止mysql和redis数据不同步问题
        try:
            res.append({int(item[1]): goods[int(item[0])]})
        except Exception as e:
            pass
    print(res)

    return res

# 商品排行榜数据视图
class GoodRank(APIView):
    def get(self, request):
        # 获取排名前N的数据
        get_res = get_top_n(10)

        res = []
        # 遍历进行序列化
        for dic in get_res:
            for k, v in dic.items():
                # 序列化
                data = GoodsSer(v).data
                # 将商品点击数附加到商品序列化数据中
                data['clicks'] = k
                res.append(data)

        # 返回
        return Response(res)


# 新的商品参与排名
class GoodsClick(APIView):
    def get(self, request):
        # 接收参数
        id = request.GET.get('id', None)
        # 修改商品点击数
        r.zincrby('good_rank', 1, int(id))

        get_top_n(10)
        return Response({'msg': '访问加1'})

# 商品检索接口
class GoodsSearch(APIView):
    def get(self, request):
        # 接收参数
        word = request.GET.get('word', None)
        print(word)

        # 检索  模糊匹配 __icontains大小写不敏感  __contains大小写敏感
        # 管道 代表 or 并且 &
        # 翻译sql  select * from goods where (name like '%word%' or desc like '%word%') and (parms like '%word%')
        # goodslist = Goods.objects.filter(Q(parms__icontains=word), Q(name__icontains=word) | Q(desc__icontains=word))
        goodslist = Goods.objects.filter(Q(name__icontains=word) | Q(desc__icontains=word))

        # 序列化
        goods_ser = GoodsSer(goodslist, many=True)
        print(goods_ser.data)
        return Response(goods_ser.data)


# 对结果集美化方法
def dictfetchall(cursor):
    # 获取游标描述
    desc = cursor.description
    return [
        dict(zip( [col[0] for col in desc], row))  # 列表推导时 行转列
        for row in cursor.fetchall()
    ]

# 商品关注接口（查询用户关注过的商品列表）
class UidFlow(View):
    def get(self, request):
        uid = request.GET.get('uid', None)
        # gid = request.GET.get('gid', None)

        # 建立游标对象
        cursor = connection.cursor()

        # 执行sql语句  查询用户关注的商品
        cursor.execute('select a.id, a.name, a.create_time from goods a left join userflow b on a.id = b.gid where b.uid = %s' % str(uid))
        # 查询关注该商品的关注用户
        # cursor.execute('select a.username from user a left join userflow b on a.id = b.uid where b.gid = %s' % str(gid))

        # 获取结果
        # res = cursor.fetchall()
        # 美化结果
        res = dictfetchall(cursor)

        # 返回结果  手动序列化 防止中文乱码   default=str,时间格式转为字符串
        return HttpResponse(json.dumps(res, ensure_ascii=False, indent=4, default=str), content_type='application/json')

# 关注商品接口（并取消关注）
class GetFlow(APIView):
    def get(self, request):
        # 商品ID
        id = request.GET.get('id', None)
        # 用户ID
        uid = request.GET.get('uid', None)
        # 判断取关还是关注  0为取关  1关注
        type = request.GET.get('type', 1)

        if int(type) == 1:
            # 关注
            userflow = UserFlow(gid=int(id), uid=int(uid))
            try:
                userflow.save()
                msg = '关注成功'

                # 查询商品  关注数加1并保存
                goods = Goods.objects.filter(id=int(id)).first()
                goods.flow = goods.flow + 1
                goods.save()
            except Exception as e:
                print(str(e))
                return Response({'msg': '不能重复关注'})

        elif int(type) == 0:
            # 单独取关
            UserFlow.objects.filter(gid=int(id), uid=int(uid)).delete()  # 删除
            msg = '取关成功'

            # 查询商品  关注数减1并保存
            goods = Goods.objects.filter(id=int(id)).first()
            goods.flow = goods.flow - 1
            goods.save()

        else:
            # 批量取关
            ids = request.GET.get('ids', None)
            # 批量操作  gid__in([2,3,4])
            UserFlow.objects.filter(uid=int(uid)).delete()

            # 强转 eval语义化
            id_list = eval(ids)
            # 遍历重新关注
            for id in id_list:
                userflow = UserFlow(gid=int(id), uid=int(uid))
                userflow.save()
            msg = '批量删除关注成功'


        return Response({'msg': msg})


# 用户列表
class UserList(APIView):
    def get(self, request):
        # 查询
        user = User.objects.filter()
        # 序列化
        user_ser = UserSer(user, many=True)
        return Response(user_ser.data)


# 评论列表
class CommentList(APIView):
    def get(self, request):
        # 商品ID
        id = request.GET.get('id', None)

        # 当前页
        page = int(request.GET.get('page', 1))
        # 每页多少条数据
        size = int(request.GET.get('size', 1))
        # 定义从那开始切
        data_start = (page - 1) * size
        # 定义切到哪
        data_end = page * size

        # 查询评论
        comment = Comment.objects.filter(gid=int(id)).order_by('-id')[data_start:data_end]  # order_by 倒序用减号

        # 查询总数
        count = Comment.objects.count()

        # 序列化
        comment_ser = CommentSer(comment, many=True)

        res = {}
        res['total'] = count
        res['data'] = comment_ser.data
        return Response(res)


# 反序列化入库(添加商品评论)
class InsertComment(APIView):
    def post(self, request):
        # 初始化参数
        # print(request.data)

        uid = request.POST.get('uid', None)
        print(uid)

        if r.get(str(uid)):
            return Response({'code': 403, 'msg': '您评论的太快了'})

        comment = CommentSer(data=request.data)
        # 数据校验
        if comment.is_valid():
            # 数据入库
            # comment.create(comment.data)
            comment.save()

            # 插入评论标记  规定评论时间
            r.set(str(uid), '123')
            r.expire(str(uid), 30)  # 防止恶意评论
        return Response({'code': 200, 'msg': '恭喜评论成功'})


# redis 统计在线人数
class GetOnLine(APIView):
    def get(self, request):
        # 获取客户端Ip
        if 'HTTP_X_FORWARDED_FOR' in request.META:
            ip = request.META.get('HTTP_X_FORWARDED_FOR')
        else:
            ip = request.META.get('REMOTE_ADDR')

        # 对用户IP进行存储
        r.sadd("online", ip)

        # 设置超时时间 超时时常单位秒
        r.expire('online', 20)

        # 获取在线人数数量  一个集合
        myset = r.smembers('online')

        # 返回数据  取集合的长度
        return Response({'online': len(myset)})



# 导入MongoDB
import pymongo
# 建立mongo的链接对象
mongo_client = pymongo.MongoClient(host='localhost', port=27017)
# 导入bson库 as起别名
from bson import json_util as jsonb

# 修改mongo数据库中的标签数据
class UpdateTags(APIView):
    def get(self, request):
        # 接受数据
        id = request.GET.get('id', None)
        tags = request.GET.get('tags', None)
        tags = tags.split(',')
        # 连接数据库
        db = mongo_client.md
        table = db.mytag
        # 开始修改
        table.update({"gid": str(id)}, {"$set": {"tags": tags}})
        res = {}
        res['code'] = 200
        res['msg'] = '修改成功'

        return Response(res)

# 获取商品标签接口  链接别的库需要用View
class GetTags(View):
    def get(self, request):
        id = request.GET.get('id', None)

        # 建立数据库对象
        db = mongo_client.md
        # 建立表对象
        table = db.mytag

        # 查询数据
        res = table.find_one({'gid': str(id)})
        return HttpResponse(jsonb.dumps(res, ensure_ascii=False))  #  中文不能编译 ensure_ascii=False 防止中文转换，以免除乱码

# 商品标签添加接口  MongoDB
class InsertTags(APIView):
    def get(self, request):
        # 接收商品数据
        id = request.GET.get('id', None)
        tags = request.GET.get('tags', None)
        # print(tags)
        tags = tags.split(",")  # 字符串转换成list与MongoDB吻合
        # tags = ','.join(tags)  # 列表转换字符串
        # print(tags)

        # 建立数据库对象
        db = mongo_client.md
        # 建立表对象
        table = db.mytag

        # 排重操作
        # 查询
        res = table.find({'gid': str(id)}).count()  # 查询count数量
        if res > 0:
            return Response({'code': 300, 'msg': '重复数据'})
        else:
            # 入库操作
            table.insert({'gid': str(id), 'tags': tags})
            return Response({'code': 200, 'msg': '入库成功'})


# 单个商品信息接口
class GoodInfo(APIView):
    def get(self, request):
        # 当前页
        id = int(request.GET.get('id'))
        # 查询数据
        good = Goods.objects.filter(id=id).first()
        # 序列化操作
        good_ser = GoodsSer(good)  # 单个查询时不需要many=True
        return Response(good_ser.data)

# 修改商品信息
class UpdateGood(APIView):
    def get(self, request):
        # 接收参数
        id = request.GET.get('id', 'null')
        name = request.GET.get('name', 'null')
        desc = request.GET.get('desc', 'null')
        parms = request.GET.get('parms', 'null')
        cate_id = request.GET.get('cate_id', 'null')
        price = request.GET.get('price', 'null')

        # 入库
        Goods.objects.filter(id=id).update(name=name, desc=desc, price=price, parms=parms, cate_id=cate_id)
        res = {}
        res['code'] = 200
        res['msg'] = '恭喜， 修改成功'

        return Response(res)

# 商品列表页  分页
class GoodsList(APIView):
    def get(self, request):
        # 当前页
        page = int(request.GET.get('page', 1))
        # 一页多少条商品
        size = int(request.GET.get('size', 2))
        # 定义从哪开始切
        data_start = (page - 1) * size
        # 定义切到哪
        data_end = page * size

        # 查询数据
        goodslist = Goods.objects.all()[data_start:data_end]
        # 查询总数量
        count = Goods.objects.count()

        # 序列化
        goods_ser = GoodsSer(goodslist, many=True)
        res = {}
        res['total'] = count  # 返回总数
        res['data'] = goods_ser.data  # 返回数据
        return Response(res)


# 插入商品
class InsertGoods(APIView):
    def get(self, request):
        # 接收参数
        name = request.GET.get('name', 'null')
        desc = request.GET.get('desc', 'null')
        parms = request.GET.get('parms', 'null')
        cate_id = request.GET.get('cate_id', 'null')
        price = request.GET.get('price', 'null')

        # 排重
        goods = Goods.objects.filter(name=name).first()

        if goods:
            res = {}
            res['code'] = 405
            res['msg'] = '该商品已存在'
            return Response(res)

        # 入库
        goods = Goods(name=name, desc=desc, price=price, parms=parms, cate_id=cate_id)
        goods.save()

        # 查询商品ID
        goods = Goods.objects.filter(name=name).first()

        res = {}
        res['code'] = 200
        res['id'] = goods.id
        res['msg'] = '恭喜， 添加成功'

        return Response(res)


# 幻灯片接口
class PicsList(APIView):
    def get(self, request):
        # 查询数据
        pics = Pics.objects.filter().values('title', 'link', 'img')
        # 序列化对象
        pics_ser = PicsSer(pics, many=True)
        return Response(pics_ser.data)


# 插入幻灯片
class InsertPics(APIView):
    def get(self, request):
        # 接收参数
        title = request.GET.get('title', 'null')
        link = request.GET.get('link', 'null')
        img = request.GET.get('img', 'null')

        # 排重
        pics = Pics.objects.filter(title=title).first()

        if pics:
            res = {}
            res['code'] = 405
            res['msg'] = '该活动已存在'
            return Response(res)

        # 入库
        pic = Pics(title=title, link=link, img=img)
        pic.save()
        res = {}
        res['code'] = 200
        res['msg'] = '恭喜,添加成功'
        return Response(res)



# 装饰器
def my_decorator(func):
    def wrapper(request, *args, **kwargs):
        # print('这个装饰器被调用了')
        # print('请求接口地址是%s' % request.path)
        #
        # # 判断jwt逻辑
        # uid = request.GET.get('uid')
        # clinet_jwt = request.GET.get('jwt', None)
        # decode_jwt = jwt.decode(clinet_jwt, '123', algorithms=['HS256'])
        # if(decode_jwt['uid'] != str(uid)):
        #     return HttpResponse('你篡改了用户ID')

        return func(request, *args, **kwargs)
    return wrapper

# 类装饰器调用装饰器
from django.utils.decorators import method_decorator

# 用户信息类
class UserInfo(APIView):
    @method_decorator(my_decorator)  # 调用装饰器
    def get(self, request):
        uid = request.GET.get('uid')
        # 查询数据
        user = User.objects.filter(id=uid).first()
        # 序列化对象
        user_ser = UserSer(user)
        res = user_ser.data
        return Response(res)

# 更新用户数据
class UpdateUser(APIView):
    def get(self, request):
        img = request.GET.get('img')
        uid = request.GET.get('uid')
        # 查询数据
        user = User.objects.get(id=int(uid))
        user.img = img
        user.save()
        return Response({'code': 200, 'msg': '更新成功'})

# 定义七牛云存储接口
from qiniu import Auth

class QiNiu(APIView):
    def get(self, request):
        # 定义秘钥
        # q = Auth('AK', 'SK')
        q = Auth('TMp1YYt7ec0baD7o1JBOF6hYCmmJ8TXGbdi6LM_A',
                 'igx1uly_3n16slUxXWvZy0Abe9lQCwhwlfNMLJUZ'
                 )
        # 指定上传空间
        # token = q.upload_token('空间名称')
        token = q.upload_token('upload9')
        # print(token)  # token 上传文件的凭证、令牌
        # 返回前端
        res = {}
        res['token'] = token
        return Response(res)

class UploadPas(APIView):
    def get(self, request):
        uid = request.GET.get('uid')
        password = request.GET.get('password')
        User.objects.filter(pk=uid).update(password=make_password(password))
        res = {}
        res['code'] = 200
        res['message'] = '修改成功'
        return Response(res)

# 定义上传视图类
class UploadFile(View):
    def post(self, request):
        # 接收参数
        img = request.FILES.get('file')
        # 建立文件流对象
        f = open(os.path.join(UPLOAD_ROOT, '', img.name), 'wb')
        # 写入服务器端
        for chunk in img.chunks():
            f.write(chunk)
        f.close()
        # 打开
        im = Image.open('./static/upload/' + img.name)
        # 设置字体
        fnt = ImageFont.truetype(font='C:\Windows\\Fonts\\Arial.ttf', size=15)
        # 花板
        d = ImageDraw.Draw(im)
        # 修改
        d.text((0, 0), '1906fjk', font=fnt, fill=(76, 234, 124, 180))
        # 存储图片
        im.save('./static/upload/' + img.name)
        # 返回文件名
        return HttpResponse(json.dumps({'filename': img.name}, ensure_ascii=False), content_type='application/json')


# 微博回调
def we_back(request):
    # 获取code
    code = request.GET.get('code')
    # 定义微博认证地址
    access_token_url = 'https://api.weibo.com/oauth2/access_token'
    # 参数
    res = requests.post(
        access_token_url,
        data={
            'client_id': '3035970630',
            'client_secret': 'cf1266f23c63aa9f65b3f9182b4e2eaa',
            'grant_type': 'authorization_code',
            'code': code,
            'redirect_uri': 'http://127.0.0.1:8000/md_admin/weibo'
        }
    )
    res = json.loads(res.text)
    # print(res)

    # 获取新浪微博昵称
    result = requests.get('https://api.weibo.com/2/users/show.json', params={
        'access_token': res['access_token'], 'uid': res['uid']
    })
    # print(result.text)

    result = json.loads(result.text)

    # 判断该用户是否登陆过
    user = User.objects.filter(username=str(result['name'])).first()

    sina_id = ''
    user_id = ''
    if user:
        sina_id = user.username
        user_id = user.id
    else:
        # 手动创建
        user = User(username=str(result['name']), password='')
        user.save()
        sina_id = result['name']
        # 查询刚刚入库的新账号ID
        user = User.objects.filter(username=str(result['name'])).first()
        user_id = user.id

    return redirect('http://localhost:8080?sina_id='+sina_id+'&uid='+str(user_id))  # redirect跳转
    # return HttpResponse(result['name'])

# 登录接口
class Login(APIView):
    def get(self, request):
        # 接收参数
        username = request.GET.get('username', 'null')
        password = request.GET.get('password', 'null')
        # 查询数据
        user = User.objects.filter(username=username, password=make_password(password)).first()
        # 判断
        if user:
            res = {}
            res['code'] = 200
            res['msg'] = '登录成功'
            res['username'] = user.username
            res['uid'] = user.id
            res['type'] = user.type
            # 加入jwt令牌机制
            encode_jwt = jwt.encode({'uid': str(user.id)}, '123', algorithm='HS256')
            encode_str = str(encode_jwt, 'utf-8')
            res['jwt'] = encode_str
            return Response(res)
        else:
            res = {}
            res['code'] = 405
            res['msg'] = '登录失败'
            return Response(res)




# 定义验证码类
class MyCode(View):
    # 定义随机颜色
    def get_random_color(self):
        R = random.randrange(255)
        G = random.randrange(255)
        B = random.randrange(255)
        return (R,G,B)
    # 获取验证码视图
    def get(self, request):
        # 画布
        img_size = (120, 50)
        # 定义画图对象
        image = Image.new('RGB', img_size, 'white')
        # 定义画笔对象
        draw = ImageDraw.Draw(image, 'RGB')
        # 定义随机字符串
        source = '0123456789'
        # 容器
        code_str = ''
        # 自定义字体
        my_font = ImageFont.truetype(font='C:\Windows\\Fonts\\Arial.ttf', size=15)
        for i in range(4):
            # 获取随机颜色
            text_color = self.get_random_color()
            # 获取随机字符串长度下标
            tmp_num = random.randrange(len(source))
            # 获取字符串
            random_str = source[tmp_num]
            # 添加到容器中
            code_str += random_str
            # 将字符串添加到画布中
            draw.text((10+30*i, 20), random_str, text_color, font=my_font)
        # 遍历缓存区
        buf = io.BytesIO()
        # 将验证码存储到redis中
        r.set('code', code_str)
        # 存储到session中
        request.session['code'] = code_str
        # 保存图片
        image.save(buf, 'png')
        return HttpResponse(buf.getvalue(), 'image/png')




# md5加密方法
def make_password(mypass):
    # 生成md5对象
    md5 = hashlib.md5()
    # 定义机密对象
    sign_str = str(mypass)
    # 转码
    sign_utf8 = sign_str.encode(encoding="utf-8")
    # 加密
    md5.update(sign_utf8)
    # 生成密码串
    md5_server = md5.hexdigest()
    return md5_server

# 注册模块
class Reg(APIView):
    def get(self, request):
        # 接收参数
        username = request.GET.get('username', 'null')
        password = request.GET.get('password', 'null')
        code = request.GET.get('code', 'null')
        # print(username, password)

        # 获取验证码
        redis_code = r.get('code')
        # 转码
        redis_code = redis_code.decode('utf-8')
        # print(redis_code)

        # 获取session中的验证码
        session_code = request.session.get('code')
        print(session_code)
        # 判断验证码是否正确
        if code != redis_code:
            res= {}
            res['code'] = 405
            res['msg'] = '验证码输入错误'
            return Response(res)

        # 排重
        user = User.objects.filter(username=username).first()
        if user:
            res = {}
            res['code'] = 405
            res['msg'] = '该用户已存在'
            return Response(res)
        # 入库
        user = User(username=username, password=make_password(password))
        user.save()
        res = {}
        res['code'] = 200
        res['msg'] = '注册成功'
        return Response(res)


#方法视图
def myindex(request):

    return HttpResponse('这里是首页')


# 类视图
class MyView(View):
    greeting = '你好'
    def get(self, request):
        title = '你好Django'
        num = 100
        li = [1, 2, 3, 4, 5]
        dic = {'name': '汽车', 'price': 100}
        return render(request, 'test.html', locals())
        # return HttpResponse(self.greeting)

class U_Index(View):
    def get(self, request):
        user = User.objects.filter().all()
        return render(request, 'u_index.html', locals())

class Add_userView(View):
    def get(self, request):
        return render(request, 'user_add.html', locals())

    def post(self, request):
        username = request.POST.get('username')
        password = request.POST.get('password')
        img = request.FILES.get('img')
        type = request.POST.get('type')
        user = User()
        user.username = username
        user.password = password
        user.img = img
        user.type = type
        user.save()
        return redirect('/index/', locals())

class Up(View):
    def get(self, request, id):
        user = User.objects.get(id=id)
        return render(request, 'u_update.html', locals())
    def post(self, request, id):
        user = User.objects.get(id=id)
        username = request.POST.get('username')
        password = request.POST.get('password')
        img = request.POST.get('img')
        type = request.POST.get('type')
        user.username = username
        user.password = password
        user.img = img
        user.type = type
        user.save()
        return redirect('index/', locals())
